﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace CellarDoor.Gui.Elements
{
    public class UiElementEventArgs : EventArgs
    {
        public UiElement Element { get; set; }

        public UiElementEventArgs(UiElement element)
            : base()
        {
            this.Element = element;
        }
    }

    public class UiElementCollection : Collection<UiElement>
    {
        private UiElement parent;

        public event EventHandler<UiElementEventArgs> ControlAdded;

        public event EventHandler<UiElementEventArgs> ControlRemoved;

        /// <summary>
        /// Creates a new child collection
        /// </summary>
        /// <param name="parent">
        /// Parent object of the child objects returned in the collection.
        /// </param>
        public UiElementCollection(UiElement parent)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }
            this.parent = parent;
        }

        /// <summary>
        /// Removes all children from the collection by setting their parents to null
        /// </summary>
        protected override void ClearItems()
        {
            foreach (UiElement child in this)
            {
                this.SetParent(child, default(UiElement));
            }
            base.ClearItems();
        }

        /// <summary>
        /// Inserts a child object into the collection at the specified index.
        /// </summary>
        /// <param name="index">The position in the collection.</param>
        /// <param name="item">The child object being inserted.</param>
        protected override void InsertItem(int index, UiElement item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (base.IndexOf(item) != -1)
            {
                throw new ArgumentException("Cannot add same control multiple times");
            }
            if (this.GetParent(item) != null)
            {
                this.GetParent(item).Elements.RemoveItem(Items.IndexOf(item));
            }

            this.ValidateBeforeAdd(item, this.parent);
            base.InsertItem(index, item);
            this.SetParent(item, this.parent);
            
            if (item != null)
            {
                OnControlAdded(item);
            }
        }

        /// <summary>
        /// Removes a child object from the collection.
        /// </summary>
        /// <param name="index">The index of the item being removed.</param>
        protected override void RemoveItem(int index)
        {
            UiElement item = base[index];

            UiElement child = base[index];
            base.RemoveItem(index);
            this.SetParent(child, default(UiElement));
            
            if (item != null)
            {
                OnControlRemoved(item);
            }
        }

        /// <summary>
        /// Modifies the value of the child object at the specified location.
        /// </summary>
        /// <param name="index">The index of the child object being modified.</param>
        /// <param name="item">The new value for the child object.</param>
        protected override void SetItem(int index, UiElement item)
        {
            throw new NotSupportedException("Cannot set control into ControlCollection");
        }

        /// <summary>
        /// Checks whether the given child can be added to given parent
        /// </summary>
        /// <param name="child"></param>
        /// <param name="parent"></param>
        protected virtual void ValidateBeforeAdd(UiElement child, UiElement parent)
        {
            if (child == null)
            {
                throw new ArgumentNullException("item");
            }
            if (this.GetParent(child) != null)
            {
                throw new InvalidOperationException(string.Format("Child {0} already has Parent", child));
            }
        }

        /// <summary>
        /// Modifies the value of the parent object of the specified child object.
        /// </summary>
        /// <param name="child">The child of the parent being modified.</param>
        /// <param name="parent">The new value for the parent object.</param>
        protected void SetParent(UiElement child, UiElement parent)
        {
            child.Parent = parent;
        }

        /// <summary>
        /// Gets the parent of a child object.
        /// </summary>
        /// <param name="child">The child of the parent being retrieved.</param>
        /// <returns>The parent of the child object.</returns>
        protected UiElement GetParent(UiElement child)
        {
            return child.Parent;
        }

        private void OnControlAdded(UiElement control)
        {
            if (ControlAdded != null)
            {
                ControlAdded(this, new UiElementEventArgs(control));
            }
        }

        private void OnControlRemoved(UiElement control)
        {
            if (ControlRemoved != null)
            {
                ControlRemoved(this, new UiElementEventArgs(control));
            }
        }
    }
}
